/*
 * Created on Jul 12, 2007
 */
package com.solusiq.me.db;

import java.util.Vector;

import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordComparator;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordFilter;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;

/**
 * @author Amru Mubaidillah
 *
 */
public class Database {
	private final String dbName;
	private Class builder;
	
	private RecordStore recordStore;
	
	private RecordFilter filter;
	private RecordComparator comparator;

	/**
	 * 
	 * @param databaseName as the record store name
	 * @param builder class which implement Recordable Interface
	 * @throws IllegalArgumentException when builder is not implementation of Recordable interface
	 */
	public Database(String databaseName, Class builder) throws IllegalArgumentException {
	    if (!builder.isAssignableFrom(Recordable.class))
	    	throw new IllegalArgumentException("Invalid Recordable class");
	    this.dbName = databaseName;
	    this.builder = builder;
	}
	
	/**
	 * set default filter for this database
	 * 
	 * @param filter RecordFilter
	 */
	public void setFilter(RecordFilter filter) {
	    this.filter = filter;
	}

	/**
	 * set the default comparator for this database
	 * @param comparator
	 */
	public void setComparator(RecordComparator comparator) {
	    this.comparator = comparator;
	}
	
	/**
	 * open a record store
	 * 
	 * @param dbname
	 * @return null if has a RecordStoreException
	 */
	public static RecordStore load(String dbname) {
		try {
			return RecordStore.openRecordStore(dbname, true);
		} catch (RecordStoreException rse) {
		}
		return null;
	}
	
	/**
	 * delete all contents and open the RecordStore
	 *
	 */
	public void cleanUp() {
		try {
			if (recordStore != null)
				recordStore.closeRecordStore();
		} catch (RecordStoreNotOpenException e) {
		} catch (RecordStoreException e) {
		}
	    if (Database.delete(this.dbName))
	        recordStore = Database.load(this.dbName);
	}
	
	/**
	 * delete record store by the name specified 
	 * @param dbname
	 * @return true if success
	 */
	public static boolean delete(String dbname) {
	    try {
            RecordStore.deleteRecordStore(dbname);
            return true;
        } catch (RecordStoreNotFoundException e) {
        } catch (RecordStoreException e) {
        }
        return false;
	}
	
	/**
	 * 
	 * @param record recordable object which need to update
	 * 
	 * @throws Exception, any exceptions found thrown as Exception
	 */
	public void updateRecord(Recordable record) throws Exception {
		byte data[] = record.serialize();
		try {
			recordStore.setRecord(record.getRecordId(), data, 0, data.length);
			return;
		} catch (RecordStoreNotOpenException e) {
			e.printStackTrace();
		} catch (InvalidRecordIDException e) {
			e.printStackTrace();
		} catch (RecordStoreFullException e) {
			e.printStackTrace();
		} catch (RecordStoreException e) {
			e.printStackTrace();
		}
		throw new Exception("gagal");
	}
	
	/**
	 * @param record to be added
	 * @return record number, and -1 if an exception/error occured
	 */
	public int addRecord(Recordable record) {
		byte[] b = record.serialize();
		try {
		    if (recordStore == null)
		        recordStore = load(this.dbName);
			int newId = recordStore.addRecord(b, 0, b.length);
			record.setRecordId(newId);
			return newId;
		} catch (RecordStoreNotOpenException e) {
			e.printStackTrace();
		} catch (RecordStoreFullException e) {
			e.printStackTrace();
		} catch (RecordStoreException e) {
			e.printStackTrace();
		}
		return -1;
	}
	
	/**
	 * delete a record by passing an recordable object which will be deleted.
	 * 
	 * @param record
	 * @return true if successfully deleted, or false if any exceptions thrown.
	 */
	public boolean deleteRecord(Recordable record) {
		try {
			recordStore.deleteRecord(record.getRecordId());
			return true;
		} catch (RecordStoreNotOpenException e) {
			e.printStackTrace();
		} catch (InvalidRecordIDException e) {
			e.printStackTrace();
		} catch (RecordStoreException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public void close() {
		try {
			recordStore.closeRecordStore();
			recordStore = null;
		} catch (RecordStoreNotOpenException e) {
		} catch (RecordStoreException e) {
		}
	}
	
	public Vector getAllRecords() {
	    return getAllRecords(null);
	}
	
	/**
	 * 
	 * @param id record number you want to retrieve. This number is Record Store number (not always sequence number in recordstore).
	 * 
	 * @return Recordable instance by id specified
	 * @throws RecordStoreNotOpenException
	 * @throws InvalidRecordIDException
	 * @throws RecordStoreException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	public Recordable getRecord(int id) throws RecordStoreNotOpenException, InvalidRecordIDException, RecordStoreException, InstantiationException, IllegalAccessException {
		if (recordStore == null) recordStore = load(this.dbName);
		Recordable baru = (Recordable) builder.newInstance();
		baru.unserialize(recordStore.getRecord(id));
		baru.setRecordId(id);
	    return baru;
	}
	
	/**
	 * retrieved set of records by RecordFilter specified.
	 *  
	 * 
	 * @param filter if null, it will get all records from RecordStore
	 * @return list of filtered records
	 */
	protected Vector getAllRecords(RecordFilter filter) {
	    
		try {
		    if (recordStore == null) recordStore = Database.load(this.dbName);
			RecordEnumeration re = recordStore.enumerateRecords(filter, comparator,
					true);
			Vector list = new Vector();
			while (re.hasNextElement())
				try {
					list.addElement( getRecord(re.nextRecordId()) );
				} catch (InstantiationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			return list;
		} catch (RecordStoreNotOpenException e) {
			e.printStackTrace();
		} catch (InvalidRecordIDException e) {
			e.printStackTrace();
		} catch (RecordStoreException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * @return number of records of current "database"
	 */
	public int count() {
		try {
			if (recordStore == null) recordStore = Database.load(this.dbName);
			return recordStore.getNumRecords();
		} catch (RecordStoreNotOpenException e) {
			e.printStackTrace();
		}
		return -1;
	}
}
